/*
 * Copyright (c) 2010, Brookhaven National Laboratory
 * All rights reserved.
 * The software in this package is published under the terms
 * of the BSD license a copy of which has been included with
 * this distribution in the license.txt file.
 */


package org.oxtypes.protobuf.impl;

import java.io.IOException;
import java.util.List;
import java.util.LinkedList;

import com.google.protobuf.Descriptors.*;
import com.google.protobuf.ByteString;
import com.google.protobuf.WireFormat.*;
import com.google.protobuf.CodedInputStream;

import org.omg.dds.type.TypeKind;
import org.omg.dds.type.dynamic.DynamicType;

import org.oxtypes.DynamicData;

import org.oxtypes.DynamicTypeFactory;
import org.oxtypes.DynamicDataFactory;

/**
 *
 */
public class RepeatedFieldBuilders {

    public RepeatedFieldBuilders() {
    }

    public RepeatedFieldBuilder createBuilder(FieldType type) {

        switch(type){
            case DOUBLE  : return new DoubleBuilder();
            case FLOAT   : return new FloatBuilder();
            case FIXED64 : return new Fixed64Builder();
            case FIXED32 : return new Fixed32Builder();
            case SFIXED64: return new SFixed64Builder();
            case SFIXED32: return new SFixed32Builder();
            case INT64   : return new Int64Builder();
            case INT32   : return new Int32Builder();
            case UINT64  : return new UInt64Builder();
            case UINT32  : return new UInt32Builder();
            case SINT64  : return new SInt64Builder();
            case SINT32  : return new SInt32Builder();
            case BOOL    : return new BoolBuilder();
            case ENUM    : return new EnumBuilder();
            case STRING  : return new StringBuilder();
            case BYTES   : return new BytesBuilder();
        }

        throw new RuntimeException(
            "There is no way to get here, but the compiler thinks otherwise.");
    }

    public interface RepeatedFieldBuilder {
      public void readFieldNoTag(CodedInputStream input) throws IOException;
      public void setDynmaicData(int id, DynamicData dd);
    }

    class DoubleBuilder implements RepeatedFieldBuilder {

        List<Double> values = new LinkedList<Double>();

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readDouble());
        }

        public void setDynmaicData(int id, DynamicData dd){
            int length = values.size();

            if(length == 0) return;

            double elements[] = new double[length];
            
            int counter = 0;
            for(Double value: values){
                elements[counter++] = value.doubleValue();
            }

            dd.setFloat64Values(id, elements, 0, length);
        }
    }

    class FloatBuilder implements RepeatedFieldBuilder {

        List<Float> values = new LinkedList<Float>();

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readFloat());
        }

        public void setDynmaicData(int id, DynamicData dd){

            int length = values.size();

            if(length == 0) return;

            float elements[] = new float[length];

            int counter = 0;
            for(Float value: values){
                elements[counter++] = value.floatValue();
            }

            dd.setFloat32Values(id, elements, 0, length);
        }
    }

    abstract class LongBuilder implements RepeatedFieldBuilder {

        List<Long> values = new LinkedList<Long>();

        public void setDynmaicData(int id, DynamicData dd){

            int length = values.size();

            if(length == 0) return;

            long elements[] = new long[length];

            int counter = 0;
            for(Long value: values){
                elements[counter++] = value.longValue();
            }

            dd.setInt64Values(id, elements, 0, length);
        }
    }

    class Fixed64Builder extends LongBuilder {

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readFixed64());
        }

    }

    abstract class IntegerBuilder implements RepeatedFieldBuilder {

        List<Integer> values = new LinkedList<Integer>();

        public void setDynmaicData(int id, DynamicData dd){

            int length = values.size();

            if(length == 0) return;

            int elements[] = new int[length];

            int counter = 0;
            for(Integer value: values){
                elements[counter++] = value.intValue();
            }

            dd.setInt32Values(id, elements, 0, length);
        }
    }

    class Fixed32Builder extends IntegerBuilder {

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readFixed32());
        }
    }

    class SFixed64Builder extends LongBuilder {

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readSFixed64());
        }
    }

    class SFixed32Builder extends IntegerBuilder {

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readSFixed32());
        }
    }

    class Int64Builder extends LongBuilder {

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readInt64());
        }
    }
    
    class Int32Builder extends IntegerBuilder {

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readInt32());
        }
    }

    class UInt64Builder extends LongBuilder {

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readUInt64());
        }
    }

    class UInt32Builder extends IntegerBuilder {

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readUInt32());
        }
    }

    class SInt64Builder extends LongBuilder {

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readSInt64());
        }
    }

    class SInt32Builder extends IntegerBuilder {

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readSInt32());
        }
    }

    class BoolBuilder implements RepeatedFieldBuilder {

        List<Boolean> values = new LinkedList<Boolean>();

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readBool());
        }

        public void setDynmaicData(int id, DynamicData dd){

            int length = values.size();

            if(length == 0) return;

            boolean elements[] = new boolean[length];

            int counter = 0;
            for(Boolean value: values){
                elements[counter++] = value.booleanValue();
            }

            dd.setBooleanValues(id, elements, 0, length);
        }
    }

    class EnumBuilder extends IntegerBuilder {

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readEnum());
        }
    }

    class StringBuilder implements RepeatedFieldBuilder {

        List<String> values = new LinkedList<String>();

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readString());
        }

        public void setDynmaicData(int id, DynamicData dd){
            dd.setStringValues(id, values);
        }
    }

    class BytesBuilder implements RepeatedFieldBuilder {

        DynamicType bytesType;
        DynamicType bytesSeqType;
        List<ByteString> values = new LinkedList<ByteString>();

        public BytesBuilder(){
            DynamicTypeFactory dtf = DynamicTypeFactory.getInstance();
            DynamicType byteType = dtf.getPrimitiveType(TypeKind.BYTE_TYPE);
            bytesType = dtf.createSequenceType(byteType, 0);
            bytesSeqType = dtf.createSequenceType(bytesType, 0);
        }

        public void readFieldNoTag(CodedInputStream input) throws IOException {
            values.add(input.readBytes());
        }

        public void setDynmaicData(int id, DynamicData dd){

            int length = values.size();

            if(length == 0) return;

            DynamicDataFactory ddf = DynamicDataFactory.getInstance();

            DynamicData bytesSeq = ddf.createData(bytesSeqType);
            bytesSeq.setLength(length);
            
            int counter = 0;
            for(ByteString byteString: values){
                DynamicData bytes = ddf.createData(bytesType);
                bytes.setByteValues(0, byteString.toByteArray(), 0, byteString.size());
                bytesSeq.setComplexValue(counter++, bytes);
            }
            dd.setComplexValue(id, bytesSeq);
        }
    }


}
